#include "ccms/nr/node_stats.h"
#include "ccms/nr/exception.h"
#include "arlib/exception/invalid_argument_exception.h"
//#include "arlib/utilities/logging/logging.h"
#include <unistd.h>
#include <math.h>
#include <vector>
#include <algorithm>
#include <cstring>
#include <linux/version.h>

#define THROW_INVALID_ARGUMENT_EXCEPTION(reason) throw arlib::exception::InvalidArgumentException(__FILE__, __LINE__, reason)

using namespace ccms::nr;

namespace ccms {
namespace nr {

NodeStats::NodeStats(void)
{
}

void NodeStats::update(void)
{
    processesTab.update();
    cpuStats.update();
    memStats.update();
}

unsigned int NodeStats::getTotalCpuUsage(const CpuUsageMode mode) const
{
    return ((unsigned int) roundf((cpuStats.getCpuTimeDiff(mode)
        / (cpuStats.getCpuTimeDiffTotal() * 1.0)) * 100));
}

unsigned long NodeStats::getTotalMemUsage(const MemUsageMode mode) const
{
    return memStats.getMemUsage(mode);
}

unsigned int NodeStats::getProcessCpuUsage(const pid_t pid, const CpuUsageMode mode) const
{
    unsigned int ret;
    unsigned long long cpuTimeDiffTotal;

    const Process& process = processesTab.getProcess(pid);
    cpuTimeDiffTotal = cpuStats.getCpuTimeDiffTotal();

    switch(mode) {
        case CPU_USAGE_MODE_USER:
            ret = (unsigned int) roundf((process.getUserTimeDiff() / (cpuTimeDiffTotal * 1.0)) * 100);
            break;
        case CPU_USAGE_MODE_SYSTEM:
            ret = (unsigned int) roundf((process.getSystemTimeDiff() / (cpuTimeDiffTotal * 1.0)) * 100);
            break;
        default:
            THROW_INVALID_ARGUMENT_EXCEPTION("Invalid mode");
    }

    // dirty hack, but there's not much we can do about it;
    // see ccms::nr::Process::normalize for more details
    return (ret > 100) ? 100 : ret;
}

unsigned long NodeStats::getProcessMemUsage(const pid_t pid, const MemUsageMode mode) const
{
    unsigned long ret;
#if !(LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25))
    static const long pagesize = sysconf(_SC_PAGESIZE);
#endif

    if(mode != MEM_USAGE_MODE_PHYS_USED)
        THROW_INVALID_ARGUMENT_EXCEPTION("Invalid mode");

    const Process& process = processesTab.getProcess(pid);

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25))
    ret = process.getPss();
#else
    ret = process.getRss() * pagesize
        / 1024; // in kilobytes
#endif

    return ret;
}

void NodeStats::registerWatchedProcess(const pid_t pid)
{
    processesTab.registerWatchedProcess(pid);
}

void NodeStats::unregisterWatchedProcess(const pid_t pid)
{
    processesTab.unregisterWatchedProcess(pid);
}

} // namespace nr
} // namespace ccms
